package munci.old.use;

import java.io.PrintStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;


public class Methods {

	public static String methodDescription(Method m) {
		StringBuilder str = new StringBuilder();
		str.append(m.getDeclaringClass().getSimpleName()).append('.').append(m.getName());
		String separator = "";
		str.append('(');
		if (!Methods.isStatic(m)) {
			str.append(m.getDeclaringClass().getSimpleName());
			separator = ", ";
		}
		for (int i = 0; i < m.getParameterTypes().length; i++) {
			str.append(separator);
			str.append(m.getParameterTypes()[i].getSimpleName());
			separator = ", ";
		}
		if (m.getReturnType() != Void.TYPE)
			str.append(" -- ").append(m.getReturnType().getSimpleName());
		str.append(')');
		return str.toString();
	}

	public static void printMethod(Method m, PrintStream stream) {
		stream.print('\t');
		stream.print(methodDescription(m));
		stream.print(m.getDeclaringClass().getCanonicalName() + "::" + m.getName());
		stream.println();
	}

	public static void printMethods(Iterable<Method> methods, PrintStream stream) {
		for (Method m : methods) {
			printMethod(m, stream);
		}
	}

	public static boolean isStatic(Method m) {
		return (m.getModifiers() & Modifier.STATIC) != 0;
	}

	public static Class<?>[] paramTypes(Method method) {
		Class<?>[] types = method.getParameterTypes();
		if (!isStatic(method)) {
			Class<?>[] t = new Class<?>[types.length + 1];
			t[0] = method.getDeclaringClass();
			System.arraycopy(types, 0, t, 1, types.length);
			return t;
		}
		return types;
	}

	public static Object invoke(Method method, Object obj, Object[] args) {
		if (obj != null || isStatic(method))
			return Try.invoke(method, obj, args);
		
		Object[] args_ = new Object[args.length - 1];
		System.arraycopy(args, 1, args_, 0, args_.length);
		return Try.invoke(method, args[0], args_);
	}
	
	public static Class<?>[] parameterTypes(Method method, boolean noObj) {
		Class<?>[] types = method.getParameterTypes();
		if (noObj || isStatic(method))
			return method.getParameterTypes();
		
		Class<?>[] types_ = new Class<?>[types.length + 1];
		System.arraycopy(types, 0, types_, 1, types.length);
		types_[0] = method.getDeclaringClass();
		return types_;
	}

	/**
	 * @param param
	 * @param arg
	 * @return is param a superclass or superinterface of arg
	 */
	public static boolean argumentValid(Class<?> param, Class<?> arg) {
		return arg == null && !param.isPrimitive()
				|| arg != null && param.isAssignableFrom(arg);
	}

	public static boolean argValid(Class<?> param, Object obj) {
		return param.isPrimitive() 
				&& (param == obj.getClass() || CONTAINERS.get(param) == obj.getClass())
			|| !param.isPrimitive() && (obj == null || param == obj.getClass());
	}
	
	public static final Map<Class<?>, Class<?>> CONTAINERS;
	
	static {
		CONTAINERS = new HashMap<Class<?>, Class<?>>();
		CONTAINERS.put(boolean.class, Boolean.class);
		CONTAINERS.put(byte.class, Byte.class);
		CONTAINERS.put(char.class, Character.class);
		CONTAINERS.put(short.class, Short.class);
		CONTAINERS.put(int.class, Integer.class);
		CONTAINERS.put(long.class, Long.class);
		CONTAINERS.put(float.class, Float.class);
		CONTAINERS.put(double.class, Double.class);
	}
}
